﻿using KinonekoSoftware.CodariaDB.IO;
using KinonekoSoftware.Extensions.Ioc;

namespace KinonekoSoftware.CodariaDB.Core
{
    public abstract class _File : _ByPass
    {
        // Images/ab404544abcc4211bc670ec6904cbca1.png
        // Images/thumb_ab404544abcc4211bc670ec6904cbca1.png
        // Avatars/ab404544abcc4211bc670ec6904cbca1.png
        // Music/ ab404544abcc4211bc670ec6904cbca1.mp3
        // Music/ ab404544abcc4211bc670ec6904cbca1.png
        // Icon/ ab404544abcc4211bc670ec6904cbca1.png

        public const string MusicFilePattern     = "{0}.mp3";
        public const string ImageFilePattern     = "{0}.png";
        public const string ThumbnailFilePattern = "thumb_{0}.png";
        public const string ThumbnailIDPattern   = "thumb_{0}";

        protected _File(string dir, ResourceType type)
        {
            BaseFolder = dir;
            Type       = type;
        }

        public static string GetDir(ILogger logger, string dir)
        {
            if (Directory.Exists(dir))
            {
                return dir;
            }

            logger?.Info($"创建目录:{dir}");
            Directory.CreateDirectory(dir);
            return dir;
        }


        public Task<IEnumerable<Resource>> GetResources()
        {
            return Task.Run<IEnumerable<Resource>>(
                                                   () => Directory.GetFiles(Folder)
                                                                  .Select(x => new FileInfo(x))
                                                                  .Select(x => new Resource
                                                                   {
                                                                       ImageID  = Path.GetFileNameWithoutExtension(x.Name),
                                                                       FileName = x.FullName,
                                                                       Type     = Type,
                                                                   }));
        }

        #region OnDatabaseOpeningInternal / OnDatabaseClosingInternal

        
        protected internal override void OnDatabaseOpeningInternal(RepositoryOpenContext context)
        {
            var logger     = context.Logger;
            var repository = context.Repository;

            
            AssetsDB  = context.Repository.Get<Occupation>(CollectionNames.AssetsDB);
            Workspace = repository.Workspace;
            Folder    = GetDir(logger, Path.Combine(repository.Workspace, BaseFolder));

            base.OnDatabaseOpeningInternal(context);
        }

        protected internal override void OnDatabaseClosingInternal(ILogger logger)
        {
            Folder   = null;
            AssetsDB = null;
            base.OnDatabaseClosingInternal(logger);
        }

        #endregion

        #region OnDatabaseOpening / OnDatabaseClosing

        protected override void OnDatabaseOpening(RepositoryOpenContext context)
        {
        }

        protected override void OnDatabaseClosing(ILogger logger)
        {
        }
        

        #endregion

        protected abstract Occupation OnCreate(string id);

        #region InsertOccupation / UpdateOccupation / RemoveOccupation

        
        public void InsertOccupation(string id)
        {
            
            if (string.IsNullOrEmpty(id))
            {
                return;
            }

            //
            //
            var occupation = OnCreate(id);


            AssetsDB.Insert(occupation);
        }
        
        public void UpdateOccupation(string id)
        {
            
            if (string.IsNullOrEmpty(id))
            {
                return;
            }

            //
            //
            var occupation = OnCreate(id);


            AssetsDB.Insert(occupation);
        }
        
        public void RemoveOccupation(string id)
        {
            if (string.IsNullOrEmpty(id))
            {
                return;
            }
            
            AssetsDB.Delete(id);
        }

        #endregion

        /// <summary>
        /// 判断是否存在指定的文件。
        /// </summary>
        /// <param name="id">文件ID</param>
        /// <returns>返回一个值，表示当前的文件是否存在。</returns>
        public bool Exists(string id)
        {
            return File.Exists(Path.Combine(Folder, id));
        }

        #region Open / OpenAsync

        
        /// <summary>
        /// 通过指定的ResourceID打开文件流。
        /// </summary>
        /// <param name="id">指定的ResourceID</param>
        /// <returns>如果文件存在，则返回一个<see cref="Stream"/>，否则返回null。</returns>
        public virtual Stream Open(string id)
        {
            var fileName = Path.Combine(Folder, string.Format(ImageFilePattern, id));

            if (File.Exists(fileName))
            {
                var stream = new FileStream(fileName, FileMode.Open);
                return stream;
            }

            return null;
        }

        /// <summary>
        /// 通过指定的ResourceID打开文件流。
        /// </summary>
        /// <param name="id">指定的ResourceID</param>
        /// <returns>如果文件存在，则返回一个<see cref="Stream"/>，否则返回null。</returns>
        public Task<Stream> OpenAsync(string id) => Task.Run(() => Open(id));

        #endregion

        #region Write / WriteAsync
        
        /// <summary>
        /// 为指定的ResourceID的资源创建文件，并写入数据。
        /// </summary>
        /// <param name="id">指定的ResourceID</param>
        /// <param name="buffer">指定要写入的数据</param>
        public virtual void Write(string id, byte[] buffer)
        {
            var fileName = Path.Combine(Folder, string.Format(ImageFilePattern, id));
            
            //
            //
            File.WriteAllBytes(fileName, buffer);
        }

        /// <summary>
        /// 为指定的ResourceID的资源创建文件，并写入数据。
        /// </summary>
        /// <param name="id">指定的ResourceID</param>
        /// <param name="buffer">指定要写入的数据</param>
        public virtual async Task WriteAsync(string id, byte[] buffer)
        {
            var fileName = Path.Combine(Folder, string.Format(ImageFilePattern, id));
            
            //
            //
            await File.WriteAllBytesAsync(fileName, buffer);
        }

        #endregion
        

        public void Delete(string id)
        {
            try
            {
                if (Exists(id))
                {
                    File.Delete(Path.Combine(Folder, id));
                }
            }
            catch(Exception ex)
            {
                Ioc.Get<ILogger>().Warn(ex.Message);
            }
        }
        
        protected ILiteCollection<Occupation> AssetsDB { get; private set; }
        protected ResourceType                Type     { get; }

        /// <summary>
        /// 相对目录，例如：\Images
        /// </summary>
        public string BaseFolder { get; }

        /// <summary>
        /// 实际目录，例如：D:\JuXiaoYou\Images
        /// </summary>
        public string Folder { get; private set; }

        /// <summary>
        /// 工作目录，例如：D:\JuXiaoYou
        /// </summary>
        public string Workspace { get; private set; }
    }
}